#include <iostream>
#include <map>
#include <list>
#include <set>
#include <vector>
#include <array>
#include <type_traits>
#include "krpc/sync.h"
#include "krpc/tcp.h"
#include "krpc/udp.h"
#include "krpc/localstream.h"
#include "krpc/localdgram.h"
#include "krpc/func.h"
#include "krpc/subcribe.h"

//KRPC::UDP com("127.0.0.1", 9000);
//KRPC::TCP com("127.0.0.1", 9000);
//KRPC::LOCALDGRAM com("unix");
KRPC::LOCALSTREAM com("unix");

void test_notargs_and_notret()
{
	KRPC::SyncFunc<void()> func("test_notargs_and_notret", com, 500);
	auto op = func();
	if (op)
		std::cout << "test_notargs_and_notret succ" << std::endl;
	else
		std::cout << "test_notargs_and_notret fail" << std::endl;
}

void test_notret()
{
	KRPC::SyncFunc<void(std::string)> func("test_notret", com, 500);
	auto op = func("test_notret called");
	if (op)
		std::cout << "test_notret succ" << std::endl;
	else
		std::cout << "test_notret fail" << std::endl;
}

void test_notargs()
{
	KRPC::SyncFunc<std::string()> func("test_notargs", com, 500);
	auto op = func();
	if (op)
		std::cout << "test_notargs: " << *op << std::endl;
	else
		std::cout << "test_notargs fail" << std::endl;
}

void test_has_args_and_ret()
{
	KRPC::SyncFunc<std::string(int, short, char, bool)> func("test_has_args_and_ret", com, 500);
	auto op = func(100000, 10000, 30, true);
	if (op)
		std::cout << "test_has_args_and_ret: " << *op << std::endl;
	else
		std::cout << "test_has_args_and_ret fail" << std::endl;
}

struct SelfClass
{
	std::vector<int> vec;
	std::list<std::set<int>> ls;
	std::map<std::string, std::array<int, 3>> ma;

	MSGPACK_DEFINE(vec, ls, ma);
};

void get_self_class()
{
	KRPC::SyncFunc<SelfClass()> func("get_self_class", com, 500);
	auto op = func();
	if (op) {
		const SelfClass &sc = *op;
		std::cout << "vec compare: " << (sc.vec == std::vector<int>{10, 20, 30}) << std::endl;
		std::cout << "ls compare: " << (sc.ls == std::list<std::set<int>>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}) << std::endl;
		std::map<std::string, std::array<int, 3>> ma;
		ma.emplace("111", std::array<int, 3>{123, 456, 789});
		std::cout << "ma compare: " << (sc.ma == ma) << std::endl;
	} else {
		std::cout << "get_self_class fail" << std::endl;
	}
}

void getString()
{
	KRPC::SyncFunc<std::string(int)> func("getString", com, 500);
	auto op = func(100);
	if (op)
		std::cout << "getString: " << *op << std::endl;
	else
		std::cout << "getString fail" << std::endl;
}

void test_sync_call()
{
	std::cout << "************" << std::endl;
	KRPC::Func func(com, 500);

	func.syncCall("test_notargs_and_notret");

	func.syncCall("test_notret", "test_notret called");

	auto op1 = func.syncCall<std::string>(std::string("test_notargs"));
	if (op1)
		std::cout << *op1 << std::endl;

	auto op2 = func.syncCall<std::string, int, short, char, bool>("test_has_args_and_ret", 100000, 60000, 30, true);
	if (op2)
		std::cout << *op2 << std::endl;

	auto op3 = func.syncCall<SelfClass>("get_self_class");
	if (op3) {
		const SelfClass &sc = *op3;
		std::cout << "vec compare: " << (sc.vec == std::vector<int>{10, 20, 30}) << std::endl;
		std::cout << "ls compare: " << (sc.ls == std::list<std::set<int>>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}) << std::endl;
		std::map<std::string, std::array<int, 3>> ma;
		ma.emplace("111", std::array<int, 3>{123, 456, 789});
		std::cout << "ma compare: " << (sc.ma == ma) << std::endl;
	}
		
	auto op4 = func.syncCall<std::string, int>("getString", 100);
	if (op4)
		std::cout << *op4 << std::endl;

	auto op5 = func.syncCall<std::string>("getVersion");
	if (op5)
		std::cout << "the version is " << *op5 << std::endl;
}

void test_class()
{
	KRPC::TCP tcp("127.0.0.1", 9001);
	KRPC::Func func(tcp, 500);
	std::cout << "tcp RPC available = " << func.available() << std::endl;
	func.syncCall("voidtest");

	func.syncCall("print", "print hello wrold!");

	auto op1 = func.syncCall<int>("count");
	if (op1)
		std::cout << "count is " << *op1 << std::endl;

	auto op2 = func.syncCall<int, int, int>("operator()", 100, 250);
	if (op2)
		std::cout << "std::function<int(int, int)> = " << *op2 << std::endl;

	auto op3 = func.syncCall<int, int>("plus", 10);
	if (op3)
		std::cout << "plus(10) = " << *op3 << std::endl;
}

int main(int argc, char const *argv[])
{
	KRPC::Heartbeat hb(com);
	std::cout << "com RPC available = " << hb.available() << std::endl;

	test_notargs_and_notret();
	test_notret();
	test_notargs();
	test_has_args_and_ret();

	get_self_class();

	getString();

	test_sync_call();

	test_class();

#if 1
	KRPC::Subcribe<std::string, int> sub("sub", com, 3000);
	sub.run([](std::string str, int i){
		std::cout << "str: " << str << ", i: " << i << std::endl;
	});
#endif
	return 0;
}
